package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

const (
	userAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36`
)

type HttpDownloader struct {
	url           string
	filename      string
	contentLength int
	acceptRanges  bool // 是否支持断点续传
	numThreads    int  // 同时下载线程数
}

func check(e error) {
	if e != nil {
		log.Println(e)
		panic(e)
	}
}

func New(url string, numThreads int) *HttpDownloader {
	var urlSplits []string = strings.Split(url, "/")
	var filename string = urlSplits[len(urlSplits)-1]

	res, err := http.Head(url)
	check(err)

	httpDownload := new(HttpDownloader)
	httpDownload.url = url
	httpDownload.contentLength = int(res.ContentLength)
	httpDownload.numThreads = numThreads
	httpDownload.filename = filename

	if len(res.Header["Accept-Ranges"]) != 0 && res.Header["Accept-Ranges"][0] == "bytes" {
		httpDownload.acceptRanges = true
	} else {
		httpDownload.acceptRanges = false
	}

	return httpDownload
}

// 下载综合调度
func (h *HttpDownloader) Download() {
	f, err := os.Create(h.filename)
	check(err)
	defer f.Close()

	if h.acceptRanges == false {
		fmt.Printf("This file doesn't approve multi-threaded download, single-thread downloading now: \n")
		resp, err := http.Get(h.url)
		check(err)
		save2file(h.filename, 0, resp)
	} else {
		var wg sync.WaitGroup
		fmt.Println("Multi-threaded downloading now: ")
		for _, ranges := range h.Split() {
			fmt.Printf("%d-%d_", ranges[0], ranges[1])
			wg.Add(1)
			go func(start, end int) {
				defer wg.Done()
				h.download(start, end)
			}(ranges[0], ranges[1])
		}
		wg.Wait()
	}
}

// 下载文件分段
func (h *HttpDownloader) Split() [][]int {
	ranges := [][]int{}
	blockSize := h.contentLength / h.numThreads
	for i := 0; i < h.numThreads; i++ {
		var start int = i * blockSize
		var end int = (i+1)*blockSize - 1
		if i == h.numThreads-1 {
			end = h.contentLength - 1
		}
		ranges = append(ranges, []int{start, end})
	}
	return ranges
}

// 多线程下载
func (h *HttpDownloader) download(start, end int) {
	req, err := http.NewRequest("GET", h.url, nil)
	check(err)
	req.Header.Set("Range", fmt.Sprintf("bytes=%v-%v", start, end))
	req.Header.Set("User-Agent", userAgent)

	resp, err := http.DefaultClient.Do(req)
	check(err)
	defer resp.Body.Close()

	save2file(h.filename, int64(start), resp)
}

// 保存文件
func save2file(filename string, offset int64, resp *http.Response) {
	f, err := os.OpenFile(filename, os.O_WRONLY, 0660)
	check(err)
	f.Seek(offset, 0)
	defer f.Close()

	content, err := ioutil.ReadAll(resp.Body)
	check(err)
	f.Write(content)
}

func main() {
	var beinput string
	var webint string
	fmt.Println("\n\n>>Please re-enter after first enter, I don't know WTF the golang is...<<\n\n")
	fmt.Println("\n--2025 Docking Corridor      WebDownloader v1.37 [golang]")
	fmt.Println(">>The latest version is downloaded by default by this downloader\n  And if you want to check the updates, please use another pointed exe to check because I don't play golang well.")
	fmt.Println(">>Source codes from: https://www.cnblogs.com/qxcheng/p/15378472.html\n    Thanks in advance.")
	fmt.Println(time.Now())
	time.Sleep(2 * time.Second)

	fmt.Println("\n>>Please choose one of the following options: (Please do not input two or more nums)")
loop: //循环位点
	fmt.Println("[DS]Download DS latest version now    [FP]Download fixingtool package    [EXI]EXIT\n[CUS]Customize")
	fmt.Printf("\nChoice: ")
	strint := beinput
	//strhttp := webint
	str1 := "DS"
	str2 := "FP"
	str3 := "EXI"
	str4 := "CUS"

	fmt.Scanf("%s", &beinput)
	switch {
	case strint == str1:
		fmt.Println("\n--Start downloading DS.\n")
		var url string = "https://CorridorDCO/DockingSubjects/download/latestversion"
		httpDownload := New(url, 4)
		fmt.Printf("Bool:%v\nContent:%d\n", httpDownload.acceptRanges, httpDownload.contentLength)
		httpDownload.Download()
		switch {
		case !httpDownload.acceptRanges:
			fmt.Println(">>DS download fail.")
		default:
			fmt.Println(">>DS download success.")
		}
	case strint == str2:
		fmt.Println("\n--Start downloading Fixingtool Package.\n")
		var url string = "https://CorridorDCO/DockingSubjects/download/fixpak"
		httpDownload := New(url, 4)
		fmt.Printf("Bool:%v\nContent:%d\n", httpDownload.acceptRanges, httpDownload.contentLength)
		httpDownload.Download()
		switch {
		case !httpDownload.acceptRanges:
			fmt.Println(">>Fixpack download fail")
		default:
			fmt.Println(">>Fixpack success")
		}
	case strint == str3:
		fmt.Println("\n--Exit.\n")
		time.Sleep(2 * time.Second)
		os.Exit(0)
	case strint == str4:
		fmt.Println(">>Customize")
		fmt.Println("Please enter the website URL: (Limited in CorridorDCO official website or other specified URLs)")
		fmt.Printf("URL: ")
		fmt.Scanf("%s", &webint)
		fmt.Println("\n--Start downloading customized contents.\n")
		var url string = webint
		httpDownload := New(url, 4)
		fmt.Printf("Bool:%v\nContent:%d\n", httpDownload.acceptRanges, httpDownload.contentLength)
		httpDownload.Download()
		switch {
		case !httpDownload.acceptRanges:
			fmt.Println(">>Download failed, please check whether your URL is correct or valid.")
			goto loop
		default:
			fmt.Println(">>Customized content download success.")
		}
	default:
		goto loop 
	}
}
